#!/usr/bin/env bash

# A rofi mode script that:
# - executes if any command is given
# - opens in browser if any url is entered
# - does basic calculations
# - searches on web
# - searches for filenames and file contents
# by Adnan Shameem (adnan360). License: MIT (Expat)
#
# Usage:
# - Download this into a dir
# - Make sure it's executable: chmod +x rofi-exec.sh
# - Make sure you have:
#     - For calc (at least one of these):
#       - bc
#       - awk
#       - python (v2 or v3)
#       - bcalc (curl -LO https://github.com/Phate6660/bcalc/blob/master/bcalc && chmod +x bcalc)
#     - For file search:
#       - the_silver_searcher (recommended)
#       - findutils (uses 'find' as a fallback for filename search only - very slow)
#     - For file content search:
#       - the_silver_searcher (recommended)
# - Run: rofi -modi "exec:./rofi-exec.sh" -show exec
#   or, use this to have both run and exec options: rofi -combi-modi "exec:./rofi-exec.sh",run -show combi
# Actually using it:
# - Enter any program or binary name installed in the system to run it. Passing params is allowed. e.g. geany test.txt
# - Simply enter any website url starting with http:// or https:// or www. to open it on browser
# - Enter math expressions like "2+2", "15-5", "10*2", "(3^2)+1" to get calculation outcomes
# - Enter your search term within single/double quotes*, then select:
#     - web: to search on the web
#     - file: to search file names
#     - filecontent: to search file content
# * You can type without quotes, but it may match with a program name or something else and execute that instead.

# Function to run command on terminal
# Params:
# $1: Command to run on term
# $2: Message before
function run_in_term() {
	mesg="Running command '$1'..."
	test ! -z "$2" && mesg="$2"
	coproc ( {{@@ terminal_exec @@}} -e "echo \"${mesg}\"; bash -i -c \"$1 && disown &>/dev/null\"; echo \"Press Ctrl+D to exit this terminal...\"; read" )
}

# Guesses if a binary is GUI app or not. Returns 1 if it is.
# The "guess" is based on availability of desktop file, mention of Terminal=true parameter etc.
# Param:
# $1: Program binary name. e.g. "nano", "vi", "geany", "inkscape"
function is_gui_binary() {
	test -f "/usr/share/applications/${1}.desktop" && desktop_file="/usr/share/applications/${1}.desktop" # GNU+Linux
	test -f "/usr/local/share/applications/${1}.desktop" && desktop_file="/usr/local/share/applications/${1}.desktop" # BSD systems
	test -f "${HOME}/.local/share/applications/${1}.desktop" && desktop_file="${HOME}/.local/share/applications/${1}.desktop" # $HOME override

	# If Terminal=true is set in desktop file, it's definitely a cli binary
	if [ ! -z "$(grep -i 'Terminal=true' \"$desktop_file\")" ]; then
		return 0
	# If Terminal=true is not set, it is most likely a GUI binary
	elif [ ! -z "$desktop_file" ]; then
		return 1
	# It could be CLI or GUI.
	# But best "guess" is that it's a CLI binary, since it doesn't have a .desktop file.
	else
		return 0
	fi
}

# Save the input path for later
input_path="$(echo "$@" | sed "s|^\~|$HOME|")"

# Handle input
# Important! It has to be before anything else is done in the script.
# Otherwise it will keep reopening the menu indefinitely!
if [ ! -z "$@" ]; then
	## Execute mode
	# For alias call to work within this script
	shopt -s expand_aliases
	test -f $HOME/.bash_aliases && source $HOME/.bash_aliases
	aliases=$(alias | grep '^alias ' | cut -d'=' -f1 | cut -d' ' -f2)
	executable=$(echo "$@" | cut -d" " -f1)
	# If the first word is an executable or an alias, execute command instead
	# There is rofi-aliases to handle alias executaion directly.
	# This allows for parameters to be executed with aliases. e.g. some-alias some-param
	if [ ! -z "$(command -v $executable)" ] || [ ! -z "$(echo $aliases | grep \"^${executable}$\")" ]; then
		is_gui_binary "$executable"
		if [ "$?" -eq "1" ]; then # Possible GUI app
			# coproc MUST be used in case of running GUI apps, otherwise system may hang
			coproc ( $@ )
		else # Possible CLI app
			run_in_term "$@"
		fi
	    exit 0

	## URL mode
	elif grep -q '^http:\|^https:\|^www\.' <<< "$@"; then # input starts with http:// or https://
		# coproc MUST be used in case of running GUI apps, otherwise system may hang
		coproc ( {{@@ webbrowser_exec @@}} "$@" )

    ## Calc mode
	# If input has numbers and symbols, process it
	elif grep -q '[\+|\*|\/|\^|-][\(]*[[:digit:]]' <<< "$@"; then
		# To strip spaces
		input=$(echo "$@" | sed 's/[[:space:]]*//g')
		if [ ! -z "$(command -v bc)" ]; then # has bc package on system
			# If there is an equal sign (=) at the end, get rid of it.
			input=$(echo "$input" | sed 's/\=//g')
			# Show the result in a notification.
			echo -en "$input = "$( echo $input | bc )"\0nonselectable\x1ftrue\n"
		elif [ ! -z "$(command -v awk)" ]; then # has awk on system
			# If there is an equal sign (=) at the end, get rid of it.
			input=$(echo "$input" | sed 's/\=//g')
			# Show the result in a notification.
			echo -en "$input = "$( awk "BEGIN {print $input}" )"\0nonselectable\x1ftrue\n"
		elif [ ! -z "$(command -v python)" ]; then # has python package on system
			# If there is an equal sign (=) at the end, get rid of it.
			# Replace "^" with "**" so that it can calculate powers.
			input=$(echo "$input" | sed 's/\=//g;s/\^/**/g')
			# Show the result in a notification.
			# The "float(1)*" part is so that Python 2 returns decimal places.
			echo -en "$input = "$( python -c "print( float(1)*$input )" )"\0nonselectable\x1ftrue\n"
		elif [ -f "$(dirname $0)/bcalc" ]; then # has bcalc
			# If there is an equal sign (=) at the end, get rid of it.
			input=$(echo "$input" | sed 's/\=//g')
			echo -en "$input = "$($(dirname $0)/bcalc "$input")"\0nonselectable\x1ftrue\n"
		else
			echo -en 'Error: No calculation utility found.\0nonselectable\x1ftrue\n'
			echo -en 'Please install bc, python or bcalc.\0nonselectable\x1ftrue\n'
		fi

		# Change prompt
		echo -en "\0prompt\x1fcalc\n"
    	exit 0

    ## Search mode - run the search on web (2nd page)
	elif grep -q '^web:' <<< "$@"; then
		search=$(echo "$@" | sed -n -e 's/^web:\(.*\)/\1/p')
		# Remove double/single quotes
		search=$(echo "$search" | sed -r "s/^\"|^'|\"$|'$//g")
		# Get the search URL to open in browser
		url=$(echo {{@@ runmenu_search_url|default('https://metager.org/meta/meta.ger3?eingabe={{q}}', true) @@}} | sed "s|{{q}}|${search}|")
		# coproc MUST be used in case of running GUI apps, otherwise system may hang
		coproc ( {{@@ webbrowser_exec @@}} "${url}" )
		exit 0

    ## Search mode - run the search locally for files (2nd page)
	elif grep -q '^name:' <<< "$@"; then
		search=$(echo "$@" | sed -n -e 's/^name:\(.*\)/\1/p')
		if [ ! -z "$(command -v ag)" ]; then
			run_in_term "ag --silent -l -g '${search}'" "Searching for '${search}'..."
		elif [ ! -z "$(command -v find)" ]; then
			run_in_term "find . -name '${search}'" "Searching for '${search}'..."
		else
			echo -en 'Error: No file search utility found\0nonselectable\x1ftrue\n'
			echo -en 'Install the_silver_searcher or findutils\0nonselectable\x1ftrue\n'
			exit 4587
		fi
		exit 0

    ## Search mode - run the search locally for file content (2nd page)
	elif grep -q '^content:' <<< "$@"; then
		search=$(echo "$@" | sed -n -e 's/^content:\(.*\)/\1/p')
		if [ ! -z "$(command -v ag)" ]; then
			run_in_term "ag --silent -l '${search}'" "Searching for '${search}' in file content..."
		else
			echo -en 'Error: No file content search utility found\0nonselectable\x1ftrue\n'
			echo -en 'Install the_silver_searcher\0nonselectable\x1ftrue\n'
			exit 4528
		fi
		exit 0

	## Search mode - offer options to do the search (1st page)
	else # Matches with nothing else, offer to search on web or file
		echo "web:$@"
		echo "name:$@"
		echo "content:$@"
		echo -en "\0prompt\x1fsearch\n"
	fi
fi
